package com.gjt.serverSocket;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URLConnection;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Iterator;

public class NonblockingSingleHTTPServer {

    private ByteBuffer contentBuffer;

    private static int port = 80;

    public NonblockingSingleHTTPServer(ByteBuffer data, String encoding, String MIMEType, int port) {
        this.port = port;
        String header = "HTTP/1.0 200 OK\r\n"
                + "Server: NonblockingSingleHTTPServer\r\n"
                + "Content-length: " + data.limit() + "\r\n"
                + "Content-type: " + MIMEType + "\r\n";
        byte[] headerBytes = header.getBytes(Charset.forName("US-ASCII"));

        ByteBuffer buffer = ByteBuffer.allocateDirect(data.limit() + headerBytes.length);
        buffer.put(headerBytes);
        buffer.put(data);

        buffer.flip(); // 翻转缓冲区
        this.contentBuffer = buffer;
    }

    public void run() throws IOException {
        // 这个方法并不打开一个新的服务器socket ,只是创建一个对象
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        if (!serverChannel.isOpen()) {
            System.out.println("通道没有打开");
            return;
        }

        serverChannel.socket().bind(new InetSocketAddress(port));
        serverChannel.configureBlocking(false);

        Selector selector = Selector.open();
        if (!selector.isOpen()) {
            System.out.println("通道没有打开");
            return;
        }

//        selector.select();
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            selector.select();

            Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
            while (keys.hasNext()) {

                SelectionKey key = keys.next();
//                if(!key.isValid())continue;
                keys.remove();

                try {
                    if (key.isAcceptable()) {
                        ServerSocketChannel channel = (ServerSocketChannel) key.channel();
                        SocketChannel client = channel.accept();
                        System.out.println("来自 ：" + client.getRemoteAddress() + "连接");
                        client.configureBlocking(false);
                        channel.register(selector, SelectionKey.OP_ACCEPT);
                    } else if (key.isReadable()) {
                        // 不费力的解析http 首部 只需要读取
                        SocketChannel client = (SocketChannel) key.channel();
                        ByteBuffer buffer = ByteBuffer.allocateDirect(4096);

                        client.read(buffer);
                        // 将通道切换到只写模式
                        key.interestOps(SelectionKey.OP_WRITE);
                        key.attach(contentBuffer.duplicate());
                    } else if (key.isWritable()) {
                        SocketChannel client = (SocketChannel) key.channel();
                        ByteBuffer buffer = (ByteBuffer) key.attachment();
                        if (buffer.hasRemaining()) {
                            client.write(buffer);
                        } else {
                            // 结束工作
                            client.close();
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("请输入参数");
            return;
        }

        try {
            // 读取要提供的文件
            String contentType = URLConnection.getFileNameMap().getContentTypeFor(args[0]);
            Path path = FileSystems.getDefault().getPath(args[0]);

            byte[] bytes = Files.readAllBytes(path);

            ByteBuffer input = ByteBuffer.wrap(bytes);


            // 设置要监听的端口
            try {
                port = Integer.parseInt(args[1]);
                if (port < 1 || port > 65535) port = 80;
            } catch (RuntimeException e) {
                e.printStackTrace();
            }

            String encoding = "UTF-8";
            if (args.length > 2) encoding = args[2];

            new NonblockingSingleHTTPServer(input, encoding, contentType, port).run();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}
